package com.boat.starter.xxljob;

import java.net.InetAddress;
import java.net.UnknownHostException;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

import com.boat.framework.base.setting.EnvProps;
import com.boat.starter.xxljob.constant.TaskConsts;
import com.boat.starter.xxljob.filter.XxljobFilter;
import com.boat.starter.xxljob.props.TaskProperties;
import com.boat.starter.xxljob.props.XxlExecutorProperties;
import com.boat.starter.xxljob.props.XxlJobProperties;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * xxl-job自动装配
 *
 * @author 周江
 */
@Slf4j
@Configuration
@EnableConfigurationProperties(TaskProperties.class)
@ConditionalOnProperty(prefix = TaskConsts.PREFIX, value = "type", havingValue = TaskConsts.TaskType.XXLJOB)
public class XxlJobAutoConfiguration implements InitializingBean{

	@Bean
	public XxljobFilter xxljobFilter() {
		return new XxljobFilter();
	}
	
	/**
	 * 配置xxl-job 执行器
	 * 
	 * @param xxlJobProperties xxl 配置
	 * @param environment      环境变量
	 * @return
	 */
	@Bean
	public XxlJobSpringExecutor xxlJobSpringExecutor(TaskProperties taskProperties, Environment environment) {
		XxlJobProperties xxlJobProperties = taskProperties.getXxljob();
		XxlExecutorProperties executor = xxlJobProperties.getExecutor();
		// 应用名默认为服务名
		String appName = executor.getAppname();
		if (StrUtil.isBlank(appName)) {
			appName = environment.getProperty("spring.application.name");
		}
		
		int port = executor.getPort();
		if (port <= 0) {
			String serverPort = environment.getProperty("server.port","8080");
			port = newJobAppPort(serverPort);
		}
		
		//优先从环境中获取，取不到则使用配置文件
		String accessToken = environment.getProperty("xxl.job.accessToken");
		if (StrUtil.isBlank(accessToken)) {
			accessToken = xxlJobProperties.getAccessToken();
		}
		String logPath = executor.getLogPath();
		// 在路径上体现应用名，避免多个应用部署同一机器时报找不到文件错误
		if (logPath.equals(XxlExecutorProperties.DEFAULT_LOGPATH)) {
			// 路径加上appname
			//为符合明心运维日志规范，日志目录格式为：应用名-backend
			String logAppName = appName;
			if (!logAppName.endsWith("backend")) {
				logAppName += "-backend";
			}
			
			//判断是否容器部署
			String localIp = null;
			if(EnvProps.instance().isK8s()) {
				try {
					localIp = InetAddress.getLocalHost().getHostAddress();
				} catch (UnknownHostException ex) {
					log.warn("获取IP失败，错误消息：" + ex.getMessage(), ex);
				}
			}
			logPath = newJobLogPath(logAppName, localIp);
		}
		log.info("xxljob logPath={}", logPath);

		// xxljob注册时应用名有特殊要求，需要应用名长度不低于4位，否则注册不上
		String xxljobRegAppname = appName;
		if (xxljobRegAppname.length() < 4) {
			xxljobRegAppname += "-service";
			log.warn("xxljob规定执行器应用名必须大于4个字符，已自动修改注册到xxljob的执行器应用名称为：{}。请配置xxljob时执行器名称保持一致，否则自动注册失效！！",
					xxljobRegAppname);
		}

		XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
		//仅用填此参数，其他都有默认值
		xxlJobSpringExecutor.setAdminAddresses(xxlJobProperties.getAdmin().getAddresses());
		xxlJobSpringExecutor.setAppname(xxljobRegAppname);
		xxlJobSpringExecutor.setAddress(executor.getAddress());
		xxlJobSpringExecutor.setIp(executor.getIp());
		xxlJobSpringExecutor.setPort(port);
		xxlJobSpringExecutor.setAccessToken(accessToken);
		xxlJobSpringExecutor.setLogPath(logPath);
		xxlJobSpringExecutor.setLogRetentionDays(executor.getLogRetentionDays());
		
		log.info("xxljob初始化开始，appname: {}, port: {}", xxljobRegAppname, port);
		return xxlJobSpringExecutor;
	}
	
	private String newJobLogPath(String appName, String localIp) {
		if(StrUtil.isBlankIfStr(localIp)) {
			return XxlExecutorProperties.DEFAULT_LOGPATH + "/" + appName + "/xxljob";
		}else {
			return XxlExecutorProperties.DEFAULT_LOGPATH + "/" + appName + "/" + localIp + "/xxljob";
		}
	}

	/**
	 * 根据现有应用服务端口号基础上，自动加1000
	 * @param serverPort
	 * @return
	 */
	private int newJobAppPort(String serverPort) {
		if (StrUtil.isNotBlank(serverPort)) {
			return Integer.valueOf(serverPort) + 1000;
		}
		throw new IllegalStateException("xxljob服务端口配置有误");
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		log.info("boat-starter-xxljob加载成功");
	}
}
